#ifndef CURLING_GUI_INCLUDED
#define CURLING_GUI_INCLUDED

#include <vector>
#include <boost/shared_ptr.hpp>
#include <extvec.hpp>
#include "config.hpp"

namespace curling {

using extvec::vector2d;
using extvec::vector3d;

class rink;
class game;

/**
 * GUI listener. See gui documentation for more information.
 */
class gui_listener
{
public:
  
  virtual void quit() = 0;
  virtual void play() = 0;
  virtual void options() = 0;
  /** Player requests exit (context specific). */
  virtual void exit() = 0;
  /** Player requests game pause. */
  virtual void pause() = 0;
  /** Player requests game resume. */
  virtual void resume() = 0;
  
  virtual void player_aim( vector2d target ) = 0;
  virtual void player_deliver( double direction, double power, double spin ) = 0;
  virtual void player_begin_sweep() = 0;
  virtual void player_end_sweep() = 0;

  /** Fade-in or fade-out completed. */
  virtual void fade_complete() = 0;
  
  /** Forwards event to renderer. */
  virtual void play_title_music() = 0;
  /** Forwards event to renderer. */
  virtual void stop_title_music() = 0;
  /** Forwards event to renderer. */
  virtual void start_arena_sound() = 0;
  /** Forwards event to renderer. */
  virtual void stop_arena_sound() = 0;
  
  virtual void focus_item() = 0;
  virtual void select_item() = 0;
  
  virtual void game_attached( boost::shared_ptr<game> g ) = 0;
  virtual void game_detached( boost::shared_ptr<game> g ) = 0;
  
  virtual void camera_animation_done() = 0;
  virtual void message_display_done() = 0;
};

/**
 * Standard no-frills camera class.
 */
class camera
{
public:
  
  inline camera( const camera& cam )
  : _fov(cam._fov), _aspect(cam._aspect), _position(cam._position), _target(cam._target), _direction(cam._direction), _up(cam._up) {}
  inline camera( double fov, double aspect )
  : _fov(fov), _aspect(aspect) {}
  
  inline double fov() const {return _fov;}
  inline double aspect() const {return _aspect;}
  inline const vector3d& position() const {return _position;}
  inline const vector3d& target() const {return _target;}
  inline void reposition( vector3d new_position ) {_position = new_position; update();}
  inline void aim( vector3d new_target ) {_target = new_target; update();}
  inline const vector3d& direction() const {return _direction;}
  inline const vector3d& up() const {return _up;}
  
  boost::shared_ptr<camera> clone() const {return boost::shared_ptr<camera>(new camera(*this));}
  
protected:
  
  inline void update()
  {
    _direction = _target - _position;
    _direction.normalize();
    vector3d left = cross(_direction, vector3d(0.0, 0.0, 1.0));
    left.normalize();
    _up = cross(left, _direction);
  }
  
private:
  
  double _fov;
  double _aspect;
  vector3d _position;
  vector3d _target;
  vector3d _direction;
  vector3d _up;
};

/**
 * In-game character such as a player of referee. */
class character_t
{
public:
  
  enum state_t
  {
    state_idle,
    state_walk
  };
  
  /** */
  character_t();
  /** */
  ~character_t();
  
  /** Get current position. */
  vector3d position() const;
  /** Move character to position. */
  void set_position( vector3d p );
  
  /** Get current animation state. */
  state_t state() const;
  
  /** Not yet implemented. */
  void animate_idle_standing();
  /** Not yet implemented. */
  void animate_idle_sitting();
  /** Walk to <target> with <speed>. */
  void animate_walk( vector3d target, double speed );
  
  /** Update animation. */
  void animate( double t, double dt );
  
private:
  
  /** Current animation state. */
  state_t _state;
  /** Current position. */
  vector3d _position;
  /** Target position of walk. */
  vector3d _target;
  /** Speed of walk [m/s].*/
  double _speed;
};

/**
 * Controls the animation of characters for different stages of a game.
 */
class game_character_controller_t
{
public:
  
  typedef boost::shared_ptr<character_t> player_t;
  
  /** */
  game_character_controller_t( boost::shared_ptr<game> g );
  /** */
  ~game_character_controller_t();
  
  /** Animate players entering the arena. */
  void animate_entrance();
  /** Animate players exiting the arena. */
  void animate_exit();
  void animate_begin_end();
  void animate_end_end();
  void animate_begin_shot();
  void animate_shot();
  void animate_end_shot();
  
  /** Update animation. */
  void animate( double t, double dt );
  
  /**
   * Returns true if the ordered animation has finished. Not all animations has
   * endings though.
   */
  bool animation_completed() const;
  
  typedef std::vector<player_t>::iterator player_iterator_t;
  
  /** Begin iterator of team a players. */
  inline std::vector<player_t>::iterator begin_team_a_players() {return _team_a.begin();}
  /** End iterator of team a players. */
  inline std::vector<player_t>::iterator end_team_a_players() {return _team_a.end();}
  /** Begin iterator of team b players. */
  inline std::vector<player_t>::iterator begin_team_b_players() {return _team_b.begin();}
  /** End iterator of team b players. */
  inline std::vector<player_t>::iterator end_team_b_players() {return _team_b.end();}
  
protected:

  enum state_t
  {
    state_idle,
    state_entrance,
    state_exit,
    state_begin_end,
    state_end_end,
    state_begin_shot,
    state_shot,
    state_end_shot
  };
  
  /*
   * The following are convenience functions that calculates different key
   * coordinates for player animation depending on the state of the game.
   *
   * All coordinates are global.
   */
  /** Global direction of play. */
  vector3d play_direction() const;
  /** Global rink bottom-center offet. */
  vector3d rink_offset() const;
  /** Skipper position at start of shot. */
  vector3d skipper_start_position() const;
  /** Left sweeper position at start of shot. */
  vector3d left_sweeper_start_position() const;
  /** Right sweeper position at start of shot. */
  vector3d right_sweeper_start_position() const;
  /** Aimer position at start of shot. */
  vector3d aimer_start_position() const;
  vector3d idle_position_1() const;
  vector3d idle_position_2() const;
  vector3d idle_position_3() const;
  vector3d idle_position_4() const;
  
private:
  
  /** Current animation state. */
  state_t _state;
  
  std::vector<player_t> _team_a;
  std::vector<player_t> _team_b;
  
  /** Game simulated. */
  boost::shared_ptr<game> _game;
};

/**
 * The GUI class (or more like the UI class since some sound and input is also
 * included) handles basic user interaction. It should be seen simply as a place
 * to translate user key input into useful commands. To do this it is possible
 * to show and hide menus, switch between screens, etc.
 *
 * The GUI should not be in control of any operations, but contains information
 * that should be presented to the user via the renderer. As such any real action
 * whether it has any affect on the game object or not, must be initiated from
 * the core object.
 *
 * Sounds and animations are mostly handled by the renderer which listens to the
 * game object for cues. The only meddling the GUI object does is to tell the
 * renderer if it should play sound or not.
 *
 * Camera movement and selection should also be controlled from the core object.
 * Camera animation can be initiated from the core object, but handled by the
 * GUI object.
 *
 * To further clarify the objective of the GUI class, it can be seen as a collection
 * of view-objects similar to those of any GUI library. Instead of doing a lot of
 * complex operations and combinations it has been consolidated into one class.
 *
 * The GUI listener class is used by the GUI object to communicate user actions 
 * to interested parties.
 */
class gui
{
public:
  
  /** Construct new GUI. */
  gui( vector2d size, double base_unit );
  /** Destruct GUI. */
  ~gui();
  
  /** Size of view in...some unknown unit. Needs to be clarified =) */
  vector2d size() const;
  /** Some scaling factor related to size(). Also needs clarification... */
  double base_unit() const;
  
  /** True if screen is being faded or is faded out. */
  bool screen_faded() const;
  /** Return how faded the screen is [0,1]. */
  double screen_fade() const;
  /**
   * Fade screen to black. Perhaps better if fade time is specified.
   *
   *\param immediate If true the screen will turn instantly black.
   */
  void fade_out( bool immediate = false );
  /** Fade back. */
  void fade_in( bool immediate = false );
  
  /** True if screen is being flashed. */
  bool screen_flashed() const;
  /** Returned how flashed the screen is [0,1]. */
  double screen_flash() const;
  /** Flash screen. */
  void flash_out();
  /** "Un-flash" screen. */
  void flash_in();
  
  /** True if credits screen is visible. */
  bool credits_screen_visible() const;
  /** Show credits screen. */
  void show_credits_screen();
  /** Hide credits screen. */
  void hide_credits_screen();
  
  /** True if main menu is visible. */
  bool main_menu_visible() const;
  /** Show main menu. */
  void show_main_menu();
  /** Hide main menu. */
  void hide_main_menu();
  
  /** True if start menu is visible. */
  bool start_menu_visible() const;
  /** Show start menu. */
  void show_start_menu();
  /** Hide start menu. */
  void hide_start_menu();
  
  /** True if options menu is visible. */
  bool options_menu_visible() const;
  /** Show options menu. */
  void show_options_menu();
  /** Hide options menu. */
  void hide_options_menu();	

  /** True if game menu is visible. */
  bool game_menu_visible() const;
  /** Show game menu. */
  void show_game_menu();
  /** Hide game menu. */
  void hide_game_menu();
  
  /** True if scoreboard is visible. */
  bool scoreboard_visible() const;
  /** Show scoreboard. */
  void show_scoreboard();
  /** Hide scoreboard. */
  void hide_scoreboard();
  
  /** True if player 1 winner screen is shown. */
  bool player_one_winner_visible() const;
  /** Show player 1 winner screen. */
  void show_player_one_winner_screen();
  /** Hide player 1 winner screen. */
  void hide_player_one_winner_screen();
  
  /** True if player 2 winner screen is shown. */
  bool player_two_winner_visible() const;
  /** Show player 2 winner screen. */
  void show_player_two_winner_screen();
  /** Hide player 2 winner screen. */
  void hide_player_two_winner_screen();
  
  /**
   * True if powermeter is visible.
   *
   * To actually use the powermeter, see setup_shot().
   */
  bool powermeter_visible() const;
  /** Show powermeter. */
  void show_powermeter();
  /** Hide powermeter. */
  void hide_powermeter();
  
  /** True if focus on "Quit" item. */
  bool main_quit_focus() const;
  /** True if focus on "Play" item. */
  bool main_play_focus() const;
  /** True if focus on "Options" item. */
  bool main_options_focus() const;
  /** True of focus on "View arena" item. */
  bool main_view_arena_focus() const;
  /** True if focus on "View credits" item. */
  bool main_view_credits_focus() const;
  
  /** True if "Human" type of player one is selected. */
  bool options_player_one_human_selected() const;
  /** True if "AI" type of player one is selected. */
  bool options_player_one_ai_selected() const;
  /** True if "Human" type of player two is selected. */
  bool options_player_two_human_selected() const;
  /** True if "AI" type of player two is selected. */
  bool options_player_two_ai_selected() const;
  
  /** True if focus on "Quit" item. */
  bool start_quit_focus() const;
  /** True if focus on "Play" item. */
  bool start_play_focus() const;
  /** True if focus on "Options" item */
  bool start_options_focus() const;

  /** True if focus on player one team selection box. */
  bool options_player_one_team_focus() const;
    /** True if focus on player one type selection box. */
  bool options_player_one_type_focus() const;
  /** True if focus on player one "Human" type item. */
  bool options_player_one_human_focus() const;
  /** True if focus on player one "AI" type item. */
  bool options_player_one_ai_focus() const;
  /** True if focus on player two team selection box. */
  bool options_player_two_team_focus() const;
   /** True if focus on player two type selection box. */
  bool options_player_two_type_focus() const;
  
  /** True if focus on player two "Human" type item. */
  bool options_player_two_human_focus() const;
  /** True if focus on player two "AI" type item. */
  bool options_player_two_ai_focus() const;
  /** True if focus on "game ends" item. */
  bool options_game_ends_focus() const;
  /** True if focus on "game stones" item. */
  bool options_game_stones_focus() const;

    /** True if focus on "Back" item. */
  bool options_back_focus() const;
 
  
  /** Get number of ends selected in start menu. */
  int options_game_ends_selected() const;
  /** Get number of stones selected in start menu. */
  int options_game_stones_selected() const;
  
  /**
   * Position player at the next stone to be played. Setup correct shot
   * parameters and their respective difficulty. All parameters ranged [0,1].
   */
  void setup_shot(
    double length, double aim, double flex,
    double length_speed, double aim_speed, double flex_speed );
  /**
   * Finish or cancel shot. Must be called when shot is done or when aborting
   * a shot so that the powermeter goes idle.
   */
  void finish_shot();
  
  /** True if powermeter power is being determined. */
  bool arrow_power_state() const;
  /** Get power of shadow arrow. */
  double shadow_arrow_power() const;
  /** Get current power of shot arrow. */
  double shot_arrow_power() const;
  /** True if powermeter direction is being determined. */
  bool arrow_direction_state() const;
  /** Get direction of shadow arrow. */
  double shadow_arrow_direction() const;
  /** Get current direction of shot arrow. */
  double shot_arrow_direction() const;
  /** True is powermeter spin is being determined. */
  bool arrow_spin_state() const;
  /** Get spin of shadow arrow. */
  double shadow_arrow_spin() const;
  /** Get spin of shot arrow. */
  double shot_arrow_spin() const;
  
  bool game_menu_continue_focus() const;
  bool game_menu_end_focus() const;
  
  /** Get rink used for all lanes not being played. */
  boost::shared_ptr<rink> background_rink();
  
  void attach_game( boost::shared_ptr<game> new_game );
  void detach_game();
  boost::shared_ptr<game> current_game();
  
  /**
   * Get character controller of attached game. The character controller is
   * automatically created when a game is attached.
   */
  boost::shared_ptr<game_character_controller_t> game_character_controller() const;
  
  /** Get currently used camera. */
  boost::shared_ptr<camera> current_camera();
  /** Switch to a new camera view. */
  void switch_camera( boost::shared_ptr<camera> new_camera );
  
  /**
   * Smooth animation from one camera to another. Notifies completion through
   * camera_animation_done().
   */
  void animate_to_camera( boost::shared_ptr<camera> new_camera, double t );
  
  /** Get camera with arena overview. */
  boost::shared_ptr<camera> arena_camera();
  /** Get camera hovering over rink. */
  boost::shared_ptr<camera> rink_top_camera();
  /** Get camera viewing top circle. */
  boost::shared_ptr<camera> circle_top_camera();
  /** Get camera following current play. */
  boost::shared_ptr<camera> stone_camera();
  
  bool title_music_playing() const;
  void play_title_music();
  void stop_title_music();
  
  bool arena_sound() const;
  void start_arena_sound();
  void stop_arena_sound();

  /**
   * Displays a text message for t seconds, or until a key is pressed if
   * t = 0. Message completion notified through text_message_done().
   */
  void display_message( const std::string& message, double t );
  /** Remove any visible message. */
  void remove_message();
  /** Current message string displayed. */
  const std::string& message_displayed() const;
  /** Current message display time left. */
  double message_time() const;
  
  /** Handle user key press. */
  void key_down( double t, int key, char character );
  /** Handle user key release. */
  void key_up( double t, int key, char character );
  /** Handle animation. */
  void animate( double t, double dt );
  
  void add_listener( boost::shared_ptr<gui_listener> listener );
  void remove_listener( boost::shared_ptr<gui_listener> listener );
  
protected:
  
  void focus_item();
  void select_item();
  
private:
  
  vector2d _size;
  double _base_unit;
  
  double _screen_fade;
  double _screen_fade_speed;
  
  double _screen_flash;
  double _screen_flash_speed;
  
  bool _credits_screen_visible;
  bool _main_menu_visible;
  bool _start_menu_visible;
  bool _options_menu_visible;
  bool _game_menu_visible;
  bool _scoreboard_visible;
  bool _powermeter_visible;

  bool _player_one_winner_visible;
  bool _player_two_winner_visible;
  
  //----------------------------------------------------------------------------
  // Power meter
  //----------------------------------------------------------------------------
  enum powermeter_state
  {
    powermeter_state_start,
    powermeter_state_power,
    powermeter_state_direction,
    powermeter_state_spin,
    powermeter_state_final
  };
  
  powermeter_state _powermeter_current_state;
  
  double _powermeter_power;
  double _powermeter_power_speed;
  double _powermeter_current_power;
  double _powermeter_direction;
  double _powermeter_direction_speed;
  double _powermeter_current_direction;
  double _powermeter_spin;
  double _powermeter_spin_speed;
  double _powermeter_current_spin;
  /** Used to control if powermeter is going left or right. */
  double _powermeter_mode;

  //----------------------------------------------------------------------------
  // Main menu
  //----------------------------------------------------------------------------
  enum main_menu_item
  {
    main_menu_item_quit,
    main_menu_item_play,
    main_menu_item_options,
    main_menu_item_view_arena,
    main_menu_item_view_credits
  };
  
  main_menu_item _main_menu_selected_item;
  
  //----------------------------------------------------------------------------
  // Start menu
  //----------------------------------------------------------------------------
  enum start_menu_item
  {
    start_menu_item_quit,
    start_menu_item_play,
	start_menu_item_options
    /*start_menu_item_player_one_team,
    start_menu_item_player_one_type,
    start_menu_item_player_two_team,
    start_menu_item_player_two_type,
    start_menu_item_game_ends,
    start_menu_item_game_stones*/
  };

   start_menu_item _start_menu_selected_item;

    //----------------------------------------------------------------------------
  // Options menu
  //----------------------------------------------------------------------------
  enum options_menu_item
  {
    options_menu_item_player1_type,
    options_menu_item_player2_type,
    options_menu_item_player1_team,
    options_menu_item_player2_team,
    options_menu_item_game_ends,
    options_menu_item_game_stones,
	options_menu_item_back,
	options_menu_item_none
  };
  
  options_menu_item _options_menu_selected_item;
 
  
  bool _player_one_human;
  bool _player_two_human;
  
  int _start_game_ends;
  int _start_game_stones;
  
  //----------------------------------------------------------------------------
  // Game menu
  //----------------------------------------------------------------------------
  enum game_menu_item
  {
    game_menu_item_continue,
    game_menu_item_end
  };
  
  game_menu_item _game_menu_selected_item;
  
  /**
   * The background rink is used when in the start menu or other such menus,
   * making a nice transition to the real rink when starting a game.
   */
  boost::shared_ptr<rink> _background_rink;
  
  /** The game currently being played. */
  boost::shared_ptr<game> _current_game;
  
  /** Character controller associated to the current game. */
  boost::shared_ptr<game_character_controller_t> _game_character_controller;
  
  //----------------------------------------------------------------------------
  // Came work
  //----------------------------------------------------------------------------
  
  /** Currently active camera. */
  boost::shared_ptr<camera> _current_camera;
  boost::shared_ptr<camera> _arena_camera;
  boost::shared_ptr<camera> _rink_top_camera;
  boost::shared_ptr<camera> _circle_top_camera;
  boost::shared_ptr<camera> _stone_camera;
  
  /** Animated camera. Current camera during animation. */
  boost::shared_ptr<camera> _animated_camera;
  /** Animation start camera. Copy of current camera. */
  boost::shared_ptr<camera> _animation_start_camera;
  /** Animation target camera. To become current camera. */
  boost::shared_ptr<camera> _animation_target_camera;
  /** Total length of camera animation. */
  double _camera_animation_length;
  /** Current time of camera animation. */
  double _camera_animation_time;
  
  //----------------------------------------------------------------------------
  // Message display
  //----------------------------------------------------------------------------
  std::string _message;
  double _message_time;
  
  //----------------------------------------------------------------------------
  // Sounds
  //----------------------------------------------------------------------------
  
  /** True if title music is being played. */
  bool _title_music_playing;
  /** True if arena sound is on. */
  bool _arena_sound;
  
  /** Listeners */
  std::vector< boost::shared_ptr<gui_listener> > _listeners;
};

}

#endif